using System;
using System.Collections.Generic;

class myUtil
{
    //Computes the dot-product of the two vectors u and v.
    public static double computeDotproduct (myVector3D u, myVector3D v)
    {
        return u.dX * v.dX + u.dY * v.dY + u.dZ * v.dZ;
    }

    //Computes the vector-product of the two vectors u and v and put into variable "result".
    public static void computeVectorproduct (myVector3D u, myVector3D v, myVector3D result)
    {
        result.dX = u.dY * v.dZ - u.dZ * v.dY;
        result.dY = u.dZ * v.dX - u.dX * v.dZ;
        result.dZ = u.dX * v.dY - u.dY * v.dX;
    }

    //computes the normal vector of the plane spanned by v1, v2, v3 and put in variable "vec".
    public static void computeNormal(myVector3D v1, myVector3D v2, myVector3D vec)
    {
        computeVectorproduct(v1, v2, vec);
    }

    public static void computePlaneOfFace(myFace f, myPlane3D result)
    {
        result.normal = new myVector3D();
        myUtil.computeNormal(f.adjacent_halfedge.source, f.adjacent_halfedge.next.source, f.adjacent_halfedge.next.next.source, result.normal);
        result.p0 = f.adjacent_halfedge.source.point;
    }
    //computes the normal vector of the plane spanned by v1, v2, v3 and put in variable "vec".
    public static void computeNormal(myVertex v1, myVertex v2, myVertex v3, myVector3D vec)
    {
        computeVectorproduct(myVector3D.MakeVector(v1.point, v2.point), myVector3D.MakeVector(v1.point, v3.point), vec);
    }

    //Computes the plane containing the triangle and put in variable "result".
    public static void computePlane(myTriangle3D t, myPlane3D result)
    {
        myVector3D normal = new myVector3D();
        myVertex v1 = new myVertex ();
        myVertex v2 = new myVertex ();
        myVertex v3 = new myVertex ();
        
        v1.point = t.p1;
        v2.point = t.p2;
        v3.point = t.p3;

        myUtil.computeNormal(v1, v2, v3, normal);

        result.normal = normal;
        result.p0 = t.p1;

    }

    //computes the intersection of the line variable "line" with the plane variable "plane". Returns the value of t so that the intersection point is line.p + t*line.v.
    public static double computeIntersection(myLine3D line, myPlane3D plane)
    {
        myVector3D n0 = plane.normal;
        myPoint3D p1 = plane.p0;
        myPoint3D p0 = line.p;
        myVector3D v0 = line.v;

        double t = (p0.X * n0.dX + p0.Y * n0.dY + p0.Z * n0.dZ - p1.X * n0.dX - p1.Y * n0.dY - p1.Z * n0.dZ) / (n0.dX * v0.dX + n0.dY * v0.dY + n0.dZ * v0.dZ);

        return t;
    }

    //Computes the closest point between a point and a plane in 3D and put in variable "result".
    // Porjection of p on h
    public static void computeClosest(myPoint3D q, myPlane3D p, myPoint3D result)
    {
        // A revoir definition incorrecte
        myVector3D n0 = p.normal;
        myPoint3D p0 = p.p0;

        //  q = p0 + t.no       E a la droite 
        // (q - p0) . n0 = 0    E au plan

        myLine3D line = new myLine3D();
        line.p = p0;
        line.v = n0;

        double t = computeIntersection(line, p);

        result = q + t * n0;        
    }

    /// Rotate the point given by p around the line given by l by angle theta, and store it in the point given by output.
    ///You will have to compute this as we did in the class by composing seven matrices.
    public static void computeRotation(myPoint3D p, myLine3D l, double theta, myPoint3D output)
    {
        l.v.normalize();

        double a = l.p.X;
        double b = l.p.Y;
        double c = l.p.Z;
        double x = p.X;
        double y = p.Y;
        double z = p.Z;
        double u = l.v.dX;
        double v = l.v.dY;
        double w = l.v.dZ;

        output.X = (a * (v * v + w * w) - u * (b * v + c * w - u * x - v * y - w * z)) * (1 - Math.Cos(theta)) + x * Math.Cos(theta) + (-c * v + b * w - w * y + v * z) * Math.Sin(theta);
        output.Y = (b * (u * u + w * w) - v * (a * u + c * w - u * x - v * y - w * z)) * (1 - Math.Cos(theta)) + y * Math.Cos(theta) + ( c * u - a * w + w * x - u * z)  * Math.Sin(theta);
        output.Z = (c * (u * u + v * v) - w * (a * u + b * v - u * x - v * y - w * z)) * (1 - Math.Cos(theta)) + z * Math.Cos(theta) + (-b * u + a * v - v * x + u * y) * Math.Sin(theta);
    }
}

